<?php

namespace Kangcg\Application\Validate;


use Kangcg\Application\Component;
use Kangcg\Application\Validate\Exception\RuleNotException;
use Kangcg\Application\Validate\Exception\SafeException;
use Kangcg\Application\Validate\Exception\ValidateException;
use Kangcg\Application\Validate\Rules\RuleInterface;

abstract class Validator extends Component
{
    protected $_validator = [
        'required' => [
            'class' => 'Kangcg\Application\Validate\Rules\Required',
            'message' => ":attribute 不能为空！"
        ],

        'if_required' => [
            'class' => 'Kangcg\Application\Validate\Rules\IfRequired',
            'message' => ":attribute 当:message 存在时不能为空！"
        ],

        'date' => [
            'class' => 'Kangcg\Application\Validate\Rules\Date',
            'message' => ":attribute 日期格式应为【:message】！"
        ],

        'min' => [
            'class' => 'Kangcg\Application\Validate\Rules\Min',
            'message' => ":attribute 不能小于【:message】！"
        ],

        'max' => [
            'class' => 'Kangcg\Application\Validate\Rules\Max',
            'message' => ":attribute 不能大于【:message】！"
        ],

        'preg' => [
            'class' => 'Kangcg\Application\Validate\Rules\Preg',
            'message' => ":attribute 验证不通过！"
        ],

        'length' => [
            'class' => 'Kangcg\Application\Validate\Rules\Length',
            'message' => ":attribute 长度不能超过【:message】！"
        ],

        'len' => [
            'class' => 'Kangcg\Application\Validate\Rules\Length',
            'message' => ":attribute 长度不能超过【:message】！"
        ],

        'safe' => [
            'class' => 'Kangcg\Application\Validate\Rules\Safe',
        ],

        'string' => [
            'class' => 'Kangcg\Application\Validate\Rules\Str',
            'message' => ":attribute 期望是个字符串！"
        ],

        'array' => [
            'class' => 'Kangcg\Application\Validate\Rules\Arr',
            'message' => ":attribute 期望是个数组！"
        ],

        'bool' => [
            'class' => 'Kangcg\Application\Validate\Rules\Boolean',
            'message' => ":attribute 期望是个布尔值"
        ],

        'integer' => [
            'class' => 'Kangcg\Application\Validate\Rules\Integer',
            'message' => ":attribute 期望是个整数"
        ],

        'numeric' => [
            'class' => 'Kangcg\Application\Validate\Rules\Integer',
            'message' => ":attribute 期望是个数字"
        ],

        'amount' => [
            'class' => 'Kangcg\Application\Validate\Rules\Integer',
            'message' => ":attribute 期望是个金额数字"
        ],

        'notin' => [
            'class' => 'Kangcg\Application\Validate\Rules\NotIn',
            'message' => ":attribute 取值不应在【:message】！"
        ],

        'id' => [
            'class' => 'Kangcg\Application\Validate\Rules\Identity',
            'message' => ":attributes 请输入正确的身份证号码！"
        ],

        'identity' => [
            'class' => 'Kangcg\Application\Validate\Rules\Identity',
            'message' => ":attributes 请输入正确的身份证号码！"
        ],

        'url' => [
            'class' => 'Kangcg\Application\Validate\Rules\Url',
            'message' => ":attributes 不是有效的路由！"
        ],
    ];

    abstract public function rules(): array;

    public function message(): array
    {
        return [];
    }

    public function label(): array
    {
        return [];
    }

    public function validate($name = '', array $input = [], array $default = []) : bool
    {
        $this->_original = $this->inputFilter($input === null ? $this->getRequestInput() : $input);
        $original = $name ? $this->_original[$name] : $this->_original;
        return $this->validateRule($this->getRules(),(array) $original);
    }

    public function validateRule(array $rules, array $input) :bool
    {
        foreach ($rules as $field => $ruleItems){
            $value = $input[$field] ?? null;
            if(! is_iterable($ruleItems)){
                $ruleItems = (array) $ruleItems;
            }

            foreach ($ruleItems as $ruleItemIndex => $ruleItem){
                $bool = false;
                try {
                    if(is_callable($ruleItem)){
                        $bool = $ruleItem($value, $field, $input, $this);
                    }elseif(is_string($ruleItemIndex)){
                        $bool = $this->getRuleInstall($ruleItemIndex, $ruleItems, $ruleItem)->run($value, $field, $input, $this);
                    }elseif(isset($this->_validator[$ruleItem])){
                        $bool = $this->getRuleInstall($ruleItem, $ruleItems)->run($value, $field, $input, $this);
                    }elseif (is_string($ruleItem)){
                        $bool = call_user_func_array([$this, $ruleItem], [$value, $field, $input, $this]);
                    }elseif (is_array($ruleItem)){
                        $bool = call_user_func_array($ruleItem, [$value, $field, $input, $this]);
                    }else{
                        throw new RuleNotException("验证规则错误！");
                    }

                    if($bool === false){
                        return false;
                    }
                }catch (SafeException $safeException){
                    //遇到安全规则时， 直接跳出验证
                    break;
                }catch (ValidateException $validateException){
                    $this->setError($validateException->getMessage(), $validateException->getCode());
                    return false;
                }
            }

            $this->_input[$field] = $value;
        }

        return  true;
    }

    public function getInput() :array
    {
        return $this->_input;
    }

    public function getOriginalInput() : array
    {
        return $this->_original;
    }
    public function getRules() : array
    {
        if($this->_rules === null){
            $this->_rules = $this->rules();
        }

        return $this->_rules;
    }

    public function inputFilter(array $data, array $default = []) : array
    {
        $data = array_merge($default, $data);
        foreach ($data as $key => $datum){
            if(is_array($datum)){
                continue;
            }

            $data[$key] = is_string($datum) ? trim($datum) : (is_array($datum) ? $this->inputFilter($datum) : $datum);
        }

        return $data;
    }

    public function getRuleInstall($rule, $rules, $config = null) : RuleInterface
    {


        if(!is_object($this->_validator[$rule])){
            $this->_validator[$rule] = $this->getInstall($this->_validator[$rule]);
        }

        if($config !== null){
            $this->_validator[$rule]->setConfig($config);
        }

        return $this->_validator[$rule];
    }

    private function getRequestInput()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'GET') {
            $data = $_GET;
        } else {
            if ($data = file_get_contents("php://input")) {
                $data = json_decode($data, true);
                $data = array_merge($_POST, $data);
            } else {
                $data = $_POST;
            }
        }

        return $data;
    }

    private $_rules = null;
    private $_input = [];
    private $_original = [];
}
